package com.marketlive.biz.account;

/*
(C) Copyright MarketLive. 2006. All rights reserved.
MarketLive is a trademark of MarketLive, Inc.
Warning: This computer program is protected by copyright law and international treaties.
Unauthorized reproduction or distribution of this program, or any portion of it, may result
in severe civil and criminal penalties, and will be prosecuted to the maximum extent
possible under the law.
*/

import org.marketlive.entity.account.IAccountSet;
import org.marketlive.entity.account.ICustomerHome;
import org.marketlive.entity.account.IHintHome;
import org.marketlive.entity.account.ICustomer;
import org.marketlive.entity.account.IHint;
import org.marketlive.entity.account.IContact;
import org.marketlive.biz.account.IAccountManager;
import org.marketlive.system.encryption.DecryptionException;
import org.marketlive.system.encryption.EncryptionException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.marketlive.entity.account.AccountSet;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.List;
import java.util.Date;
import java.util.Collections;
import java.util.Random;



/**
 * Manages account-related transactions.
 * <p>
 * This manager provides an API for customer creation and update, address book operations
 * (via the address book manager), order history, item history, and credit cards.
 */
public class AccountManager implements IAccountManager {
  /** logger for this class. */
  private static Log log = LogFactory.getLog(AccountManager.class);

  /**
   * This is the default date format pattern used for displaying order date.
   * For example this will display the date as: July 4, 2006
   */
  private static final String DEFAULT_DATE_FORMAT = "MMM d, yyyy";

  /** A reference to the customer home object. */
  private ICustomerHome customerHome;

  /**
   * Default constructor
   */
  public AccountManager() {
  }

  /**
   * Sets the {@link ICustomerHome} object.
   *
   * @param customerHome customer home object to save
   */
  public void setCustomerHome(ICustomerHome customerHome) {
    this.customerHome = customerHome;
  }

  /**
   * Returns the {@link ICustomerHome} object associated with this instance.
   *
   * @return the <code>ICustomerHome</code> object associated with this instance
   */
  public ICustomerHome getCustomerHome() {
    return customerHome;
  }

  /**
   * @inheritDoc
   * @deprecated see IAccountManager
   */
  public boolean isCustomerTaken(final String email) {
      AccountSet accountSet = new AccountSet("SET_A", "Account Set 1");
      return this.isCustomerTaken(accountSet, email);
  }

  /**
   * @inheritDoc
   */
  public boolean isCustomerTaken(IAccountSet accountSet, final String email) {
    // check if there is already a customer with this email
    if (null == email) return false;

    //ArrayList customerList = (ArrayList)customerHome.findByEmail(email.toLowerCase());
    ArrayList customerList = (ArrayList) customerHome.findByUserName(accountSet, email.toLowerCase());
    if ((customerList.size() > 0)) {
      return true;
    } else {
      return false;
    }
  }


  /**
   * @inheritDoc
   */
  public ICustomer findCustomerByEmail(IAccountSet accountSet, final String email) {
    ICustomer customer = null;
    if (null == email) return customer;
    String emailLowCaps = email.toLowerCase();

    Iterator customers = customerHome.findByEmail(accountSet, emailLowCaps).iterator();

    // loop until all customers checked or one is found with given email
    while (customers.hasNext() && customer == null) {
      ICustomer aCustomer = (ICustomer) customers.next();

      if (emailLowCaps.equals(aCustomer.getEmail())) {
        customer = aCustomer;
      }
    }
    return customer;
  }

  /**
   * @inheritDoc
   */
  public ICustomer findCustomerByPk(final String pk) {
    ICustomer customer = null;

    if (null == pk) {
      return customer;
    }

    // Changed to getByPk from findByPk in resolution of issues MLPB-2289 & MLPB-2290 - Kelly.
    // getByPk actually returns a null if the customer is not found.
    customer = (ICustomer) customerHome.getByPk(pk);

    return customer;
  }

  /**
   * @inheritDoc
   */
  public boolean updatePasswordRecoveryInfo(ICustomer customer, IHint hint, String hintAnswer) {
    String savedHintAnswer = null;
    String savedHintId = null;
    boolean retVal = false;

    if (customer != null) {
      customer.setHintAnswer(hintAnswer);
      customer.setHint(hint);
      updateCustomer(customer);
      // now check that what was put in is what was submitted
      savedHintAnswer = customer.getHintAnswer();
      savedHintId = customer.getHint().getPk().getAsString();
      if ((savedHintAnswer != null) && (savedHintId != null)) {
        retVal =  savedHintAnswer.equals(hintAnswer) && savedHintId.equals(hint.getPk().getAsString());
      }
    }
    log.debug("retVal=" + retVal);
    return retVal;
  }

  /**
   * @inheritDoc
   */
  public boolean updateCustomer(ICustomer customer, String email, String userName)
  {
    boolean retVal = false;
    if (customer != null) {
      customer.setEmail(email.toLowerCase());
      customer.setUserName(userName.toLowerCase());
      customer.setKeyCode(customerHome.getEncryptionKeyCode());
      updateCustomer(customer);
      retVal = true;
    }
    log.debug("retVal=" + retVal);
    return retVal;
  }

  /**
   * @inheritDoc
   */
  public boolean updateCustomer(ICustomer customer, String email, String userName, String password)
  {
    boolean retVal = false;

    if (customer != null) {
      customer.setEmail(email.toLowerCase());
      customer.setUserName(userName.toLowerCase());
      customer.setKeyCode(customerHome.getEncryptionKeyCode());
      customer.setPassword(password);
      updateCustomer(customer);
      retVal = true;
    }
    log.debug("retVal=" + retVal);
    return retVal;
  }

  /**
   * @inheritDoc
   */
  public boolean updateCustomer(ICustomer customer, String email, String userName, String password, boolean isGuest)
  {
    boolean retVal = false;

    if (customer != null) {
      customer.setGuest(isGuest);
      customer.setEmail(email.toLowerCase());
      customer.setUserName(userName.toLowerCase());
      customer.setKeyCode(customerHome.getEncryptionKeyCode());
      customer.setPassword(password);
      updateCustomer(customer);
    }
    log.debug("retVal=" + retVal);
    return retVal;
  }

  /**
   * @inheritDoc
   */
  public boolean updateCustomer(ICustomer customer,
                                String email,
                                String userName,
                                String password,
                                IHint hint,
                                String hintAnswer) {
    boolean retVal = false;

    if (customer != null) {
      customer.setEmail(email.toLowerCase());
      customer.setUserName(userName.toLowerCase());
      customer.setKeyCode(customerHome.getEncryptionKeyCode());
      customer.setPassword(password);
      customer.setHintAnswer(hintAnswer);
      customer.setHint(hint);
      updateCustomer(customer);
      retVal = true;
    }
    log.debug("retVal=" + retVal);
    return retVal;
  }

  /**
   * @inheritDoc
   */
  public void removeCustomer(ICustomer customer) {
    customerHome.delete(customer);
  }

  /**
   * @inheritDoc
   */
  public void updateCustomer(ICustomer customer) {
    customerHome.update(customer);
  }

  /**
   * Sets the deleted status of the given customer.
   *
   * @param customer {@link ICustomer} whose deleted status will be set
   * @param isDeleted true to mark given customer as deleted, false to mark as not deleted
   */
  public void setCustomerDeleted(ICustomer customer, boolean isDeleted) {
    customer.setDeleted(isDeleted);
  }


  /**
   * @inheritDoc
   */
  public ICustomer createCustomer(final IAccountSet accountSet,
                                  final String email,
                                  final String userName,
                                  final String password,
                                  final IHint hint,
                                  final String hintAnswer,
                                  boolean isGuest) throws Exception {

    ICustomer customer = null;
    boolean customerTaken = false;

    customer = customerHome.create(accountSet, email.toLowerCase(), userName.toLowerCase(), password);
    customer.setHint(hint);
    customer.setHintAnswer(hintAnswer);
    customer.setGuest(isGuest);

    // check whethere customer record was really created in db
    if (customer != null) {
      customerTaken = isCustomerTaken(customer);
    }

    if (customerTaken == false) {
      customer = null;
    }

    return customer;
  }


  /**
   * @inheritDoc
   */
  public ICustomer createCustomer(IAccountSet accountSet, final String email, final String userName, final String password,
                                  final boolean isGuest) {
    ICustomer customer = null;
    boolean customerTaken = false;

    customer = customerHome.create(accountSet, email.toLowerCase(), userName.toLowerCase(), password);

    customer.setGuest(isGuest);

    if (customer != null) {
      customerTaken = isCustomerTaken(customer);
    }

    // check if there is already such a customer and if so return null.
    if (customerTaken == true) {
      customer = null;
    }

    // set the customer code to the customer's pkString.
    customer.setCode("ML_" + customer.getPk().getAsString());

    return customer;
  }

  /**
   * @inheritDoc
   */
  public ICustomer findCustomer(IAccountSet accountSet, String userName) {
    Iterator customers = customerHome.findByUserName(accountSet, userName).iterator();
    ICustomer registeredCustomer = null;

    // loop until all customers checked or a registered one is found
    while (customers.hasNext() && registeredCustomer == null) {

      ICustomer aCustomer = (ICustomer) customers.next();

      if (!aCustomer.isGuest()) {
        registeredCustomer = aCustomer;
      }
    }

    return registeredCustomer;
  }


  /**
   * @inheritDoc
   *
   * @throws RuntimeException if unable to encrypt the password.
   */
  public boolean confirmCustomerPassword(ICustomer customer, String password) {
    String encryptedPassword = null;
    try {
      encryptedPassword = customerHome.encryptPassword(password);
    }
    catch (EncryptionException e) {
      throw new RuntimeException("Could not encrypt password", e);
    }
    boolean passwordsSame = false;
    try {
      passwordsSame = customerHome.comparePasswords(customer.getPassword(), encryptedPassword);
    }
    catch (DecryptionException e) {
      // Suppress the exception. If customer password is unencrypted, then encryptor will throw an exception.
    }
    return passwordsSame;
  }

  /**
   * @inheritDoc
   */
  public boolean comparePasswords(String encryptedPassword1, String encryptedPassword2) {
    boolean passwordsSame = false;
    try {
      passwordsSame = customerHome.comparePasswords(encryptedPassword1, encryptedPassword2);
    }
    catch (DecryptionException e) {
      // Suppress the exception. If password is unencrypted, then encryptor will throw an exception.
      return false;
    }
    return passwordsSame;
  }

  /**
   * @inheritDoc
   */
  private boolean isCustomerTaken(ICustomer customer) {

    String dbEmail = null;
    String dbPassword = null;
    boolean isEmailSet = false;
    boolean isPasswordSet = false;

    // check that customer was persisted to db
    dbEmail = customer.getEmail();
    if (dbEmail != null && dbEmail.trim().length() > 0) {
      isEmailSet = true;
    }

    dbPassword = customer.getPassword();
    if (dbPassword != null && dbPassword.trim().length() > 0) {
      isPasswordSet = true;
    }

    return (isEmailSet && isPasswordSet);
  }


  /**
   * @inheritDoc
   */
  public List getOrderHistoryForCustomer(Set orders) {
    ArrayList infoList = null;
    if (orders != null) {
      infoList = new ArrayList(orders);
    }
    return infoList;
  }

  /**
   * @inheritDoc
   *
   * @throws RuntimeException if unable to encrypt the password.
   */
  public String encryptCustomerPassword(String password) {
    String encryptedPassword = null;
    try {
      encryptedPassword = customerHome.encryptPassword(password);
    }
    catch (EncryptionException e) {
      throw new RuntimeException("Could not encrypt password", e);
    }
    return encryptedPassword;
  }

}
